import { APlugin } from '../types/common.interface';
import { TableDatas } from '../cors/table-datas';
import { Inject, Injectable, PluginInjectable } from '../decorators/inject';
import { Hook } from '../cors/hook';
import { NExcel } from '../types';
import { Config } from '../cors/config';
import { filter } from 'rxjs/operators';
import './row-height.plugin.less';
import { RenderTrigger } from '../cors/render-trigger';
import { Cache } from '../cors/cache';
import { TableEvent } from '../cors/table-event';
import './scroll-x.less';
import { ScaleCommand } from '../commands/scale-command';
import { ExecuteRecord } from '../cors/execute-record';

@Injectable()
@PluginInjectable('ScrollX')
export class ScrollX  extends APlugin{
    protected scrollContainer!: HTMLElement;
    protected scrollInnerEle!: HTMLElement;
    protected footerContainer!: HTMLElement;
    protected scrollSpace = 10;
    constructor(
        @Inject(NExcel.OperateContainerToken) protected ele: HTMLElement,
        protected conf: Config,
        protected tableData: TableDatas,
        protected cache: Cache,
        protected renderTrigger: RenderTrigger,
        protected tableEvent: TableEvent,
        protected hook: Hook,
        protected executeRecord: ExecuteRecord,
        protected render: RenderTrigger,
    ) {
        super()
    }
    public init() {
        this.initDom();
        this.bindDomEvent();
        this.hook.pixelRatioChange.add(() => {
            this.scrollSpace = this.conf.header.col.width / this.conf.enlarge / 8;
        });
        this.hook.afterScaleChange.add(() => {
            this.scrollSpace = this.conf.header.col.width / this.conf.enlarge / 8;
        })
        this.hook.tableDataChange.add(() => {
            this.listenChange();
        });
        this.hook.afterDeleteRow.add(() => {
            this.listenChange();
        });
        this.hook.afterDeleteCol.add(() => {
            this.listenChange();
        });
        this.hook.afterInsertRow.add(() => {
            this.listenChange();
        });
        this.hook.afterInsertCol.add(() => {
            this.listenChange();
        });
        this.hook.afterRowHeightChange.add(() => {
            this.listenChange();
        });
        this.hook.afterColWidthChange.add(() => {
            this.listenChange();
        });
        this.hook.pixelRatioChange.add(() => {
            this.listenChange();
        });
        this.hook.afterScaleChange.add(() => {
            this.listenChange();
        });
    }
    protected timmer: any= null;
    protected listenChange() {
        if(this.timmer !== null) {
            clearTimeout(this.timmer);
        }
        this.timmer = setTimeout(() => {
            let maxCol = Math.max(this.conf.defaultMaxScroll.col + 1, this.tableData.getMaxCol()+1);
            let totalWidth = 0;
            for(let i = 0; i < maxCol; i++) {
                totalWidth += this.tableData.getColWidth(i);
            }
            let scrollLeft = this.scrollContainer.scrollLeft;
            let innerWidth = Number(this.scrollInnerEle.style.width.replace('px', ''));
            let newWidth = totalWidth / this.conf.enlarge;
            this.scrollInnerEle.setAttribute('style', `width: ${newWidth}px;`);
            this.scrollContainer.scrollTop = (scrollLeft / innerWidth) * newWidth;
            let newEvt = new Event('scroll');
            this.scrollContainer.dispatchEvent(newEvt);
            this.timmer = null
        }, 100);
    }
    protected initDom() {
        this.footerContainer = document.createElement('div');
        this.footerContainer.setAttribute('class','z-excel-footer');
        this.footerContainer.innerHTML = `
            <div class="z-excel-scroll-x-container"></div>
            <div class="z-excel-others">
                <div class="z-excel-scale-content">
                    <div class="z-excel-scale-btn z-excel-scale-reduce">-</div>
                    <div class="z-excel-scale-bar">
                        <div class="z-excel-scale-dot"></div>
                    </div>
                    <div class="z-excel-scale-btn z-excel-scale-add">+</div>
                    <div class="z-excel-scale">100%</div>
                </div>
            </div>
        `;
        this.scrollContainer = document.createElement('div');
        this.scrollContainer.setAttribute('class', 'z-excel-scroll-x');
        this.scrollInnerEle = document.createElement('div');
        this.scrollContainer.append(this.scrollInnerEle);
        let maxCol = this.conf.defaultMaxScroll.col + 1;
        let defaultColWidth = this.conf.header.col.width;
        this.scrollInnerEle.setAttribute('class', 'z-excel-scroll-inner');
        this.scrollInnerEle.setAttribute('style', `width: ${maxCol * defaultColWidth / this.conf.enlarge}px;`);
        this.footerContainer.querySelector('.z-excel-scroll-x-container').append(this.scrollContainer);
        this.ele.append(this.footerContainer);
        if(!document.querySelector('.z-excel-global-mask')) {
            let ele = document.createElement('div');
            ele.setAttribute('class', 'z-excel-global-mask');
            document.querySelector('body').append(ele);
        }
        let dotEle = this.footerContainer.querySelector('.z-excel-scale-dot') as HTMLElement;
        let dotContent = this.footerContainer.querySelector('.z-excel-scale-bar');
        let percentEle = this.footerContainer.querySelector('.z-excel-scale') as HTMLElement;
        percentEle.innerText = this.conf.scale * 100 + '%';
        (() => {
            let maxLeft = dotContent.clientWidth;
            let scale = this.conf.scale;
            let left = maxLeft / 2 * scale;
            dotEle.style.left = left + 'px';
        })();
        this.hook.afterScaleChange.add(() => {
            let maxLeft = dotContent.clientWidth;
            percentEle.innerText = Math.floor(this.conf.scale * 100) + '%';
            dotEle.style.left = this.conf.scale / 2 * maxLeft + 'px';
        });
        dotEle.addEventListener('mousedown', (downEvt) => {
            let maxLeft = dotContent.clientWidth;
            let originX = downEvt.clientX;
            let originLeft = Number(dotEle.style.left.replace('px', ''));
            let maskEle = document.querySelector('.z-excel-global-mask');
            let transaction = this.begin();
            maskEle.setAttribute('style', 'display:block;');
            const onMouseMove = (moveEvt: MouseEvent) => {
                let currentX = moveEvt.clientX;
                let moveX = currentX - originX;
                let currentLeft = (() => {
                    let temp = originLeft + moveX;
                    if(temp >= maxLeft / 4 && temp <= maxLeft) {
                        return temp;
                    }
                    if(temp < maxLeft / 4) {
                        return maxLeft / 4;
                    }
                    if(temp > maxLeft) {
                        return maxLeft;
                    }
                })();
                let percent = Math.ceil(currentLeft / (maxLeft / 2) * 100);
                transaction.setScale(percent / 100);
            }
            document.addEventListener('mousemove', onMouseMove)
            const onMouseup = (upEvt: MouseEvent) => {
                document.removeEventListener('mousemove', onMouseMove);
                document.removeEventListener('mouseup', onMouseup);
                maskEle.removeAttribute('style');
                transaction.commit();
            }
            document.addEventListener('mouseup',onMouseup);
        });
        this.footerContainer.querySelector('.z-excel-scale-reduce').addEventListener('click', () => {
            if(this.conf.scale <= 0.5) {
                return;
            }
            let transaction = this.begin();
            transaction.setScale(this.conf.scale - 0.1);
            transaction.commit();
        });
        this.footerContainer.querySelector('.z-excel-scale-add').addEventListener('click', () => {
            if(this.conf.scale >= 2) {
                return;
            }
            let transaction = this.begin();
            transaction.setScale(this.conf.scale + 0.1);
            transaction.commit();
        });
    }
    protected bindDomEvent() {
        this.scrollContainer.addEventListener('scroll', (evt) => {
            let scroll = this.scrollContainer.scrollLeft * this.conf.enlarge;
            this.conf.view.col = scroll;
            this.renderTrigger.triggerRenderAll();
        });
        this.tableEvent.scrollObs$.pipe(filter((event) => {
            if(event.directive === 'right' || event.directive === 'left') {
                return true;
            }
            return false;
        })).subscribe((event: any) => {
            if(event.directive === 'left') {
                this.scrollContainer.scrollLeft = this.scrollContainer.scrollLeft + this.scrollSpace;

            } else {
                this.scrollContainer.scrollLeft = this.scrollContainer.scrollLeft - this.scrollSpace;
            }
            let newEvt = new Event('scroll',event.event);
            this.scrollContainer.dispatchEvent(newEvt)
        });
    }
    public begin() {
        let scale = this.conf.scale;
        let latestScale = -1;
        let isCommited = false;
        return {
            setScale: (val: number) => {
                if(isCommited) {
                    console.warn('已经提交，不能修改了');
                    return;
                }
                this.conf.updateScale(val);
                latestScale = val;
                this.render.triggerRenderAll();
            },
            rollback: () => {
                if(isCommited) {
                    console.warn('已经提交，不能回滚了');
                    return;
                } else {
                    isCommited = true;
                }
                if(latestScale === -1) {
                    return;
                }
                this.conf.updateScale(scale);
                this.render.triggerRenderAll();
            },
            commit: () => {
                if(isCommited) {
                    console.warn('已经提交，不能重复提交');
                    return;
                } else {
                    isCommited = true;
                }
                this.conf.scale = scale;
                let command = new ScaleCommand(this.conf,this.render,latestScale);
                this.executeRecord.execute(command);
            }
        }
    }
    public destroy(): void {
        
    }
}